import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';

/// 格式化文件大小
String formatFileSize(int size) {
  if (size <= 0) return "0B";

  const List<String> units = ['B', 'kB', 'MB', 'GB'];
  int unitIndex = 0;
  double sizeInUnit = size.toDouble();

  while (sizeInUnit >= 1024 && unitIndex < units.length - 1) {
    sizeInUnit /= 1024;
    unitIndex++;
  }

  return "${sizeInUnit.toStringAsFixed(2)}${units[unitIndex]}";
}

/// 格式化日期时间字符串。
///
/// 根据传入的 [dateTime]，判断是否为昨天的日期：
/// - 如果是昨天，则返回 "昨天HH:mm" 的格式；
/// - 否则，返回 "yyyy.MM.dd HH:mm" 的格式。
///
/// [dateTime] 是需要格式化的日期时间对象。
///
/// 返回格式化后的日期时间字符串。
String formatDateTime(DateTime dateTime) {
  final now = DateTime.now();
  final yesterday = now.subtract(const Duration(days: 1));

  // 判断传入的日期是否为昨天
  if (dateTime.year == yesterday.year &&
      dateTime.month == yesterday.month &&
      dateTime.day == yesterday.day) {
    return '昨天${DateFormat('kk:mm').format(dateTime)}';
  } else if (dateTime.year == now.year &&
      dateTime.month == now.month &&
      dateTime.day == now.day) {
    // 判断传入的时间是否距离当前时间不到15分钟
    final difference = now.difference(dateTime);
    if (difference.inMinutes < 15) {
      return '刚刚';
    } else {
      return '今天${DateFormat('kk:mm').format(dateTime)}';
    }
  } else {
    return DateFormat('yyyy.MM.dd kk:mm').format(dateTime);
  }
}

/// 线性插值
double lerp<T extends num>(T start, T end, double t) {
  t = t.clamp(0.0, 1.0);
  return (start + (end - start) * t).toDouble();
}

Function createThrottle(Function func, Duration wait) {
  Timer? timer;
  return () {
    timer ??= Timer(wait, () {
      func.call();
      timer = null;
      timer?.cancel();
    });
  };
}

Function createDebounce(Function func, Duration wait) {
  Timer? timer;
  return () {
    if (timer != null) {
      timer!.cancel(); // 取消之前的定时器
    }
    timer = Timer(wait, () {
      func.call();
    });
  };
}

enum SKLOGLevel {
  debug(SKLOGColors.cyan, 0),
  info(SKLOGColors.brightGreen, 1),
  warning(SKLOGColors.brightYellow, 2),
  error(SKLOGColors.brightRed, 3),
  ;

  final SKLOGColors color;
  final int level;
  const SKLOGLevel(this.color, this.level);
}

enum SKLOGColors {
  black('\x1B[30m'),
  red('\x1B[31m'),
  green('\x1B[32m'),
  yellow('\x1B[33m'),
  blue('\x1B[34m'),
  magenta('\x1B[35m'),
  cyan('\x1B[36m'),
  white('\x1B[37m'),
  brightBlack('\x1B[90m'),
  brightRed('\x1B[91m'),
  brightGreen('\x1B[92m'),
  brightYellow('\x1B[93m'),
  brightBlue('\x1B[94m'),
  brightMagenta('\x1B[95m'),
  brightCyan('\x1B[96m'),
  brightWhite('\x1B[97m'),
  reset('\x1B[0m'),
  ;

  final String colorStr;
  const SKLOGColors(this.colorStr);
}

SKLOGLevel defaultLevel = kDebugMode ? SKLOGLevel.debug : SKLOGLevel.info;

class _BlankLog {
  _BlankLog operator <<(dynamic message) => this;
  _BlankLog call(dynamic message) => this;
}

class SKLOG extends _BlankLog {
  final bool printAnyway;
  final SKLOGLevel level;
  final SKLOGLevel _printlevel = defaultLevel;
  final SKLOGColors? color;

  int _printLine = 1;
  static bool globalFullPath = true;
  static bool globalPrintFlag = true;

  int i = 0;
  String prefix = '';
  List<String> _list = [];

  get _shouldPrint =>
      printAnyway || (globalPrintFlag && _printlevel.level <= level.level);

  SKLOG({
    this.printAnyway = false,
    this.level = SKLOGLevel.info,
    this.color,
    String? text,
    bool? fullPath,
    bool newLine = false,
    int printLineIndex = 1,
  }) {
    if (_shouldPrint) {
      _printLine = printLineIndex;
      fullPath ??= SKLOG.globalFullPath;
      final stackTrace = StackTrace.current;
      final fileLine = stackTrace.toString().split('\n')[_printLine];
      final regexp =
          RegExp(r'^#\d+\s+(.+) \((.+\/)(.+:\d+:?\d+?)\)$', multiLine: true);
      RegExpMatch result = regexp.firstMatch(fileLine)!;
      String funName = result.group(1)!;
      String funNamePrefix = result.group(2)!;
      String fileInfo = result.group(3)!;
      prefix =
          '[${fullPath ? funNamePrefix : ''}$fileInfo] [$funName]:${newLine ? '\n     ' : ''}   ';
      _add(prefix);
      if (text != null) {
        _add(text);
      }
    }
  }

  void _add(dynamic obj) {
    String str = obj.toString();
    _list.add(str);
    i = _list.length;
    final count = i;
    Future.delayed(Duration.zero, () {
      if (count == i) _log();
    });
  }

  void _log() {
    String color = this.color?.colorStr ?? level.color.colorStr;
    _list = _list.map(
      (e) {
        var str = e;
        if (e.contains('\n')) str = e.replaceAll('\n', '\n        ');
        return str;
      },
    ).toList();
    print("$color${_list.join(' ')}${SKLOGColors.reset.colorStr}");
    _list.clear();
  }

  @override
  _BlankLog operator <<(dynamic message) {
    if (_shouldPrint) {
      _add(message);
      return this;
    } else {
      return _BlankLog();
    }
  }

  @override
  _BlankLog call(dynamic message) {
    if (_shouldPrint) {
      _add(message);
      return this;
    } else {
      return _BlankLog();
    }
  }

  static SKLOG get debug => SKLOG(level: SKLOGLevel.debug, printLineIndex: 2);

  static SKLOG get warning =>
      SKLOG(level: SKLOGLevel.warning, printLineIndex: 2);

  static SKLOG get info => SKLOG(level: SKLOGLevel.info, printLineIndex: 2);

  static SKLOG get error => SKLOG(level: SKLOGLevel.error, printLineIndex: 2);

  static void forbiddenPrint() {
    globalPrintFlag = false;
  }

  static void allowPrint() {
    globalPrintFlag = true;
  }

  static void setDefaultLevel(SKLOGLevel level) {
    defaultLevel = level;
  }
}
